package it.trekker.statistics;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import android.location.Location;
import android.location.LocationProvider;
import android.util.Log;
import it.trekker.db.ExcursionCoordinate;
import it.trekker.gps.service.IGPSListener;
import it.trekker.sensors.IOrientationListener;
import it.trekker.sensors.OrientationCoord;
import it.trekker.step.IStepDetectorListener;

public class RealTimeStatistics implements Serializable, IGPSListener, IOrientationListener, IStepDetectorListener {
	
	protected static final String TAG = RealTimeStatistics.class.getSimpleName();
	
	protected long steps = 0;

	protected List<IRealTimeCallback> realTimeListeners = null; 
	protected String excursionName = null;
	protected long excursionId;
	protected GPSStatistics statisticsManager;
	protected Calendar calendar;
	protected Location currentLocation;
	protected OrientationCoord orientation;
	protected boolean gpsEnabled;
	
	protected static RealTimeStatistics instance = null;
	
	
	public static RealTimeStatistics getSharedInstance() {
		if(instance == null)
			instance = new RealTimeStatistics();
		
		return instance;
	}
	
	
	public static RealTimeStatistics getPrivateInstance() {
		return new RealTimeStatistics();
	}
	
	
	protected RealTimeStatistics() {
		realTimeListeners = new ArrayList<IRealTimeCallback>();
		statisticsManager = new GPSStatistics();
		
		currentLocation = null;
		gpsEnabled = false;
	}
	
	
	public String getExcursionName() {
		return excursionName;
	}
	

	public void setExcursionName(String excursionName) {
		this.excursionName = excursionName;
	}
	
	
	public long getExcursionId() {
		return excursionId;
	}
	
	
	public void setExcursionId(long excursionId) {
		this.excursionId = excursionId;
	}

	
	public synchronized Location getCurrentLocation() {
		return currentLocation;
	}
	
	
	public boolean isGpsEnabled() {
		return gpsEnabled;
	}
	
	
	public synchronized void setCurrentLocation(Location currentLocation) {
		this.currentLocation = currentLocation;
	}
	
	
	
	//*****************************************************************//
	//******************* Statistics getter methods *******************//
	//*****************************************************************//
	
	
	public double getMeanPace() {
		return statisticsManager.getMeanPace();
	}
	
	
	public double getDistance() {
		return statisticsManager.getDistance();
	}
	
	
	public double getTotAltitudeDifference() {
		return statisticsManager.getTotAltitudeDifference();
	}
	
	
	public long getTime() {
		return statisticsManager.getTime();
	}

	

	//************************************************************//
	//******************* IGPSListener methods *******************//
	//************************************************************//


	@Override
	public void onLocationChanged(Location location) {
		
		setCurrentLocation(location);
		
		ExcursionCoordinate coord = new ExcursionCoordinate(location);
		calendar = Calendar.getInstance();
		coord.setTimestamp(calendar.getTime());
		
		statisticsManager.addLocation(coord);
		statisticsManager.update();
		
		notifyListeners4LocationChanged();
	}

	@Override
	public void onProviderDisabled() {
		gpsEnabled = false;
	}

	@Override
	public void onProviderEnabled() {
//		gpsEnabled = true;
	}

	@Override
	public void onStatusChanged(int status) {
		Log.i(TAG, "On status changed: " + status);
		switch(status) {
			case LocationProvider.OUT_OF_SERVICE:
			case LocationProvider.TEMPORARILY_UNAVAILABLE:
				gpsEnabled = false;
				break;
	
			case LocationProvider.AVAILABLE:
				gpsEnabled = true;
				break;
		}
	}
	
	
	
	
	//************************************************************//
	//*************** IOrientationListener methods ***************//
	//************************************************************//
	
	@Override
	public void updateOrientation(OrientationCoord coord) {
		orientation = coord;
		
		notifyListeners4OrientationChanged();
	}
	
	
	
	
	//*************************************************************//
	//*************** IStepDetectorListener methods ***************//
	//*************************************************************//
	
	@Override
	public void onStep() {
		steps++;
		notifyListeners4StepsChanged();
	}
	
	
	
	
	//*************************************************************//
	//********************** Utility methods **********************//
	//*************************************************************//
	
	public long getSteps() {
		return steps;
	}
	
	protected void notifyListeners4LocationChanged() {
		for(IRealTimeCallback mCallback : realTimeListeners) {
			AltitudeDifferenceObject diff = new AltitudeDifferenceObject();
			diff.totAltitudeDifference = statisticsManager.getTotAltitudeDifference();
			diff.upAltitudeDifference = statisticsManager.getUpAltitudeDifference();
			diff.downAltitudeDifference = statisticsManager.getDownAltitudeDifference();
			
			mCallback.altitudeChanged(statisticsManager.getCurrentAltitude());
			mCallback.distanceChanged(statisticsManager.getDistance());
			mCallback.paceChanged(statisticsManager.getPace());
			mCallback.altitudeDifferenceChanged(diff);
		}
	}
	
	protected void notifyListeners4StepsChanged() {
		for(IRealTimeCallback mCallback : realTimeListeners) {
			mCallback.stepsChanged(steps);
		}
	}
	
	protected void notifyListeners4OrientationChanged() {
		for(IRealTimeCallback mCallback : realTimeListeners) {
			mCallback.orientationChanged(orientation);
		}
	}
	
	public void registerCallback(IRealTimeCallback listener) {
		realTimeListeners.add(listener);
	}
	
	public void removeCallback(IRealTimeCallback listener) {
		realTimeListeners.remove(listener);
	}
	
	public void invalidateSharedInstance() {
		instance = null;
	}
	
	
	

	//****************************************************//
	//**************** Utility interfaces ****************//
	//****************************************************//
	
	public interface IRealTimeCallback {
		public void paceChanged(double value);
		public void distanceChanged(double value);
		public void altitudeChanged(double value);
		public void altitudeDifferenceChanged(AltitudeDifferenceObject diff);
		public void stepsChanged(long value);
		public void orientationChanged(OrientationCoord orientation);
	}
	
	
	
	
	//*************************************************//
	//**************** Utility classes ****************//
	//*************************************************//
	
	public class AltitudeDifferenceObject {
		public double totAltitudeDifference;
		public double upAltitudeDifference;
		public double downAltitudeDifference;
	}
}
